package com.smile.frame.websocket.netty.service.handler;

import cn.hutool.json.JSONUtil;
import com.smile.frame.websocket.netty.service.dto.ClientInfoDTO;
import com.smile.frame.websocket.netty.service.enums.CmdEnum;
import com.smile.frame.websocket.netty.service.dto.WebsocketMessageDTO;
import com.smile.frame.websocket.netty.service.util.BuilderMessageUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 心跳处理 handler
 *
 * @author LGC
 * @ChannelHandler.Sharable 保证处理器，在整个生命周期中就是以单例的形式存在
 */
@Component
@ChannelHandler.Sharable
public class HeartBeatHandler extends IdleStateHandler {

    private static Logger log = LoggerFactory.getLogger(HeartBeatHandler.class);
    /**
     * 最大未收到client端发送的ping请求
     */
    private final int maxUnRecPingTimes = 3;

    /**
     * 未收到client端发送的ping请求
     */
    private int unRecPingTimes = 0;

    /**
     * 多少秒后未读
     */
    private static final int READ_IDLE_GAP = 150;

    public HeartBeatHandler() {
        super(READ_IDLE_GAP, 0, 0, TimeUnit.SECONDS);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 判断消息实例
        if (!(msg instanceof TextWebSocketFrame)) {
            super.channelRead(ctx, msg);
            return;
        }
        TextWebSocketFrame textWebSocketFrame = (TextWebSocketFrame) msg;
        WebsocketMessageDTO websocketMessageDTO = JSONUtil.toBean(textWebSocketFrame.text(), WebsocketMessageDTO.class);

        ClientInfoDTO clientInfo = websocketMessageDTO.getClientInfo();
        // ping
        if (websocketMessageDTO.getCmd().equals(CmdEnum.PING.getCode())) {
            if (ctx.channel().isActive()) {
                ctx.write(BuilderMessageUtil.buildStringMessage(clientInfo, CmdEnum.PONG, CmdEnum.PONG.getInfo()));
                // 处理PING消息
                unRecPingTimes = 0;
            }
        } else {
            super.channelRead(ctx, msg);
        }

    }

    @Override
    protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {

        String type = "";
        switch (evt.state()) {
            case READER_IDLE:
                type = "读空闲";
                //读空闲的计数加1
                unRecPingTimes++;
                break;
            case WRITER_IDLE:
                type = "写空闲";
                // 不处理
                break;
            case ALL_IDLE:
                type = "读写空闲";
                // 不处理
                break;
            default:
                throw new IllegalStateException("非法状态！");
        }
        if (unRecPingTimes > maxUnRecPingTimes) {
            log.info("channel:{},客户端链接地址:{},{}秒内连续超过{}次未收到client的ping消息,等待client重连", ctx.channel().id().asLongText(), ctx.channel().remoteAddress(), READ_IDLE_GAP, unRecPingTimes);
            ctx.channel().close();
        }

    }

}